`include "timescale.v"

module onu_mac(
	//host interface
	//AHB
	input 		   	  hresetn,
	input 		   hclk,
	output [31:0]  haddr,
	output [1:0]   htrans,				//how about sel ??
	output 		   hwrite,
	output [2:0]   hsize,
	output [2:0]   hburst,
	output [3:0]   hprot,
	output [31:0]  hwdata,
	input 		   hready,
	input  [1:0]   hresp,
	input  [31:0]  hrdata,
	
	//APB
	input 		   presetn,
	input 		   pclk,
	input 		   psel,
	input 		   penable,
	input 		   pwrite,
	input  [31:0]  paddr,
	input  [31:0]  pwdata,
	output [31:0]  prdata,

	//interface for PHY : GMII 
	output 		   Tx_en, Tx_er,
	output [7:0]   Txd,
	input 		   Rx_dv, Rx_er,
	input  [7:0]   Rxd,
	input 		   Rx_clk,				//from PHY
	input 		   Gtx_clk,				//connect to RS, not from PHY	

	input 		   Col, Crs
);
	
	// reg 		   	  LatchTx; 					//the start signal


//// common signals
wire 			  reset;
assign 			  reset 	  = ~hresetn;
wire 	  [7:0]   Rxd_mac;
wire 	  [7:0]   Txd_mac;
wire 			  Rx_dv_mac;

wire 	  [4:0]   rx_state;
wire 	  [15:0]  rx_byte_cnt;

wire 	  [3:0]   rx_frame_type;

// wire 	  [7:0]   rx_frame_buffer;
wire 	  [15:0]  LLID;							//by mpcp
// wire 			  rx_abort;		//drived by onu_rx ???
wire 			  rx_fcs_ok;		//gen a pulse only when fcs is ok, only lasts for one clk
// wire 			  rx_fcs_pre_ok;

//for tx
wire 	  [7:0]   h_tx_data;
wire 			  h_tx_data_ready;

wire 	  [5:0]   tx_state;			//not useful ??

wire 			  h_tx_data_rd;	//read en by Tx module
wire 	  [15:0]  tx_byte_cnt;	//this is the addr the Tx module read from

wire 			  tx_grant;
wire 			  tx_grant_timeslot;
  
wire 	  [7:0]   tx_fifo_data_out;
wire 	  [7:0]   report_data_out;
wire 	  [7:0]   register_req_data_out;
wire 	  [7:0]   register_ack_data_out;


wire 	  [47:0]  LOCAL_MAC_ADDR;
assign 			  LOCAL_MAC_ADDR = 48'h4012_3456_789A;

wire 	  [31:0]  LocalTimeStamp;				//in mpcp
// assign 			  LocalTimeStamp = 32'h1234_5678;
////////////////////////////////////////////////////////////////////////////////////////////////////

// `define TX_CTRL [0]
// `define RX_CTRL [1]
// `define TX_BD_PTR [2]
// `define RX_BD_PTR [3]

// apb_regs U_apb_regs(
// 	.presetn				(presetn),
// 	.pclk					(pclk),
// 	.psel					(psel),
// 	.penable				(penable),
// 	.pwrite					(pwrite),
// 	.paddr					(paddr),
// 	.pwdata					(pwdata),
// 	.prdata					(prdata)
// 	);

////////////////////////////////////////////////////////////////////////////////////////////////////
// wire 			  StateReadFrame;
// // ahb_ctrl U_ahb_ctrl(
// ahb_ctrl_byte U_ahb_ctrl(				//reading Tx buffer by bytes
// 	.hresetn				(hresetn),
// 	.hclk					(hclk),
	
// 	.haddr					(haddr),
// 	.htrans					(htrans),
// 	.hwrite					(hwrite),
// 	.hsize					(hsize),
// 	.hburst					(hburst),
// 	.hprot					(hprot),
// 	.hwdata					(hwdata),
// //hsel 	
// 	.hready					(hready),
// 	.hresp					(hresp),
// 	.hrdata					(hrdata), 
	
// 	// .tx_bd_pointer			(32'h0000_0400),			//from the registers
// 	.tx_bd_pointer			(U_apb_regs.MAC_REGS`TX_BD_PTR),
// 	// .LatchTx				(LatchTx),				//the start signal
// 	.LatchTx				(U_apb_regs.MAC_REGS`TX_CTRL[0]), //the start signal
// 	.LatchWB				(U_onu_tx.StateFCS & U_onu_tx.ByteCntEq3),
	
// 	.StateReadFrame_o 		(StateReadFrame)
// 	);
// ============================== for Tx fifo ==============================
// reg 			  StateReadFrame_q;
// always@(posedge hclk)
// begin
//   StateReadFrame_q <= StateReadFrame;
// end

reg 			  tx_fifo_wr_en;
reg 	  [7:0]   tx_fifo_data_in;
// // assign 			  tx_fifo_wr_en  = StateReadFrame_q & hready;
// // assign 			  tx_fifo_data_in = hrdata[7:0];

reg 			  mpcp_clk;
initial begin
  mpcp_clk  <= 1'b0;
  forever #1 mpcp_clk = ~mpcp_clk;
end

integer 		  mpcp_cnt 	  = 100;
always@(posedge mpcp_clk)
begin
  tx_fifo_data_in <= mpcp_cnt;
  mpcp_cnt = mpcp_cnt + 1;
end

// reg 	  [15:0]  LLID;
// initial begin
//   LLID = 0;
//   #4172 LLID = 16'd3;
// end

initial begin
  tx_fifo_wr_en = 0;
  #100 tx_fifo_wr_en = 1;
  #100  tx_fifo_wr_en = 0;
end



// wire 			  tx_fifo_empty;
// wire 			  tx_fifo_full;

wire 			  tx_fifo_rd_en;
//asfifo
asfifo #(
		 .data_width(8),
		 .address_width(11)		//depth 2048
		 ) U_txfifo (
					 .data_out (tx_fifo_data_out),
					 .empty (tx_fifo_empty),
					 .read_en (tx_fifo_rd_en),
					 .clk_read (Gtx_clk),
					 
					 .data_in (tx_fifo_data_in),
					 .full	(tx_fifo_full),
					 .write_en (tx_fifo_wr_en),
					 // .clk_write (hclk),
					 .clk_write (mpcp_clk),
					 
					 .rst	(reset)
					 );

// //resolve the empty flag delayed problem
reg 	  [7:0]   tx_fifo_data_out_q;
always@(posedge Gtx_clk)
begin
  tx_fifo_data_out_q <= tx_fifo_data_out;
end

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// // onu_mux
// // generate PAUSE frame




// ////////////////////////////////////////////////////////////////////////////////////////////////////
// wire 	  [7:0]   Rxd_mac;



// ////////////////////////////////////////////////////////////////////////////////////////////////////
// // onu_classifier

// wire 			  StateData;
// wire 	  [15:0]  len_type;

// wire data_frame;
// wire oam_frame;
// wire pause_frame;
// wire gate_frame;
// wire report_frame;
// wire register_req_frame;
// wire register_frame;
// wire register_ack_frame;
// wire 			  UnicastOK;
// onu_classifier U_onu_classifier(
// 			   .reset		(~hresetn),
// 			   .Rx_clk		(Rx_clk), 
// 			   .Rx_dv		(Rx_dv_mac), 
// 			   .Rx_er		(Rx_er),
// 			   .Rxd			(Rxd_mac),
			   
// 			   .StateData(StateData),
// 			   .ByteCntEq14	(ByteCntEq14), 
// 			   .ByteCntEq15	(ByteCntEq15),
// 			   .len_type	(len_type),
	
// 			   .UnicastOK	(UnicastOK), 
// 			   .BroadcastOK	(BroadcastOK), 
// 			   .MulticastOK(MulticastOK),
// 			   .MPCP_PAUSE_MultiAddr_OK (MPCP_PAUSE_MultiAddr_OK),
// 			   .OAM_MultiAddr_OK (OAM_MultiAddr_OK),
			   
// .data_frame(data_frame),
// .oam_frame(oam_frame),
// .pause_frame(pause_frame),
// .gate_frame(gate_frame),
// .report_frame(report_frame),
// .register_req_frame(register_req_frame),
// .register_frame(register_frame),
// .register_ack_frame			(register_ack_frame)

// 	// .gate_discovery_frame	(),
// 	// .gate_normal_frame		()
	
// 	);

////////////////////////////////////////////////////////////////////////////////////////////////////
// onu_rx
// wire 			  mpcp_pause_addr_ok, oam_addr_ok;


// wire 			  rx_frame_we;
reg 			  rx_abort;

// wire 			  Rx_dv_mac;			//controlled by RS
onu_rx U_onu_rx(
	.reset					(reset),
	.Rx_clk					(Rx_clk),				//from PHY
	.Rx_dv					(Rx_dv_mac),
	.Rx_er					(Rx_er),
	.Rxd					(Rxd_mac),

	.rx_state				(rx_state),
	.rx_byte_cnt			(rx_byte_cnt),
	.rx_fcs_ok				(rx_fcs_ok),
	.rx_abort				(rx_abort)			//input 
	
	// .MAC					(48'h4012_3456_789A),
	// .transmitting			(1'b0),
	//states
	// .StateData_o			(StateData),
	// .len_type				(len_type),
	// .ByteCntEq14			(ByteCntEq14),
	// .ByteCntEq15			(ByteCntEq15),
	
	// .UnicastOK				(UnicastOK), 
	// .BroadcastOK			(BroadcastOK), 
	// .MulticastOK			(MulticastOK),
	// .MPCP_PAUSE_MultiAddr_OK(MPCP_PAUSE_MultiAddr_OK),
	// .OAM_MultiAddr_OK		(OAM_MultiAddr_OK)
	);

onu_classifier U_onu_classifier(
	.reset					(reset),
	.Rx_clk					(Rx_clk),
	.Rxd_mac 				(Rxd_mac),
	.rx_state				(rx_state),
	.rx_byte_cnt			(rx_byte_cnt),
	.rx_fcs_ok				(rx_fcs_ok),

	.rx_frame_type				(rx_frame_type),
	
	.LOCAL_MAC_ADDR			(LOCAL_MAC_ADDR)
	);
	


// assign 			  rx_frame_we = 1'b1;
////////////////////////////////////////////////////////////////////////////////////////////////////
// //mpcp module
// reg 	  [31:0]  LocalTimeStamp;
// reg 	  [31:0]  OLTTimeStamp;
// // reg 			  ts_load;
// wire 			  ts_load;
// assign 			  ts_load 	  = U_onu_rx.CrcErr == 1'b0;

// //for mux
// reg 			  mux_mode;

// always@(posedge Rx_clk or posedge reset)
// begin
//   if(reset)
// 	LocalTimeStamp <= 32'd0;
//   else if(ts_load)
// 	LocalTimeStamp <= OLTTimeStamp;
//   else 
// 	LocalTimeStamp <= LocalTimeStamp + 32'd1;
// end

// // reg 	  [7:0]   mpcp_buf [31:0];
// reg 	  [7:0]   OLT_Grant_Num;
// reg 	  [31:0]  OLT_Grant1_Time;
// reg 	  [15:0]  OLT_Grant1_Len;

// wire 			  tx_grant;						//tx grant by OLT   //launch Tx
// assign tx_grant = LocalTimeStamp == OLT_Grant1_Time;
// // assign 			  tx_grant 	  = gate_frame;


// wire 			  ByteCntEq16,ByteCntEq17,ByteCntEq18,ByteCntEq19,ByteCntEq20,ByteCntEq21,ByteCntEq22,ByteCntEq23,ByteCntEq24,ByteCntEq25,ByteCntEq26;
// assign ByteCntEq16 = U_onu_rx.ByteCnt == 16'd16;
// assign ByteCntEq17 = U_onu_rx.ByteCnt == 16'd17;
// assign ByteCntEq18 = U_onu_rx.ByteCnt == 16'd18;
// assign ByteCntEq19 = U_onu_rx.ByteCnt == 16'd19;
// assign ByteCntEq20 = U_onu_rx.ByteCnt == 16'd20;
// assign ByteCntEq21 = U_onu_rx.ByteCnt == 16'd21;
// assign ByteCntEq22 = U_onu_rx.ByteCnt == 16'd22;
// assign ByteCntEq23 = U_onu_rx.ByteCnt == 16'd23;
// assign ByteCntEq24 = U_onu_rx.ByteCnt == 16'd24;
// assign ByteCntEq25 = U_onu_rx.ByteCnt == 16'd25;
// assign ByteCntEq26 = U_onu_rx.ByteCnt == 16'd26;


// reg 			  gate_discovery_frame, gate_normal_frame;

// always@(posedge Rx_clk)
// begin
//   // ts_load <= 1'b0;
//   if(~Rx_dv_mac)
//   begin
// 	gate_discovery_frame <= 1'b0;
// 	gate_normal_frame <= 1'b0;
//   end
//   else
//   if(gate_frame)
//   begin
// 	if(ByteCntEq16)
// 	  OLTTimeStamp[31:24] <= Rxd_mac;
// 	else if(ByteCntEq17)
// 	  OLTTimeStamp[23:16] <= Rxd_mac;
// 	else if(ByteCntEq18)
// 	  OLTTimeStamp[15:8] <= Rxd_mac;
// 	else if(ByteCntEq19)
// 	begin
// 	  OLTTimeStamp[7:0] <= Rxd_mac;
// 	  // ts_load <= 1'b1;
// 	end
// 	else if(ByteCntEq20)
// 	begin
// 	  OLT_Grant_Num <= Rxd_mac;
	  
// 	  gate_discovery_frame <= Rxd_mac[3];
// 	  gate_normal_frame <= ~Rxd_mac[3];
// 	end
// 	else if(ByteCntEq21)
// 	  OLT_Grant1_Time[31:24] <= Rxd_mac;
// 	else if(ByteCntEq22)
// 	  OLT_Grant1_Time[23:16] <= Rxd_mac;
// 	else if(ByteCntEq23)
// 	  OLT_Grant1_Time[15:8] <= Rxd_mac;
// 	else if(ByteCntEq24)
// 	  OLT_Grant1_Time[7:0] <= Rxd_mac;
// 	else if(ByteCntEq25)
// 	  OLT_Grant1_Len[15:8] <= Rxd_mac;
// 	else if(ByteCntEq26)
// 	  OLT_Grant1_Len[7:0] <= Rxd_mac;
//   end
// end
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// wire 	  [7:0]   rx_fifo_data_out;
// wire 			  rx_fifo_empty;
// wire 			  rx_fifo_full;
// wire 			  rx_fifo_rd_en;

// assign 			  rx_fifo_wr_en = StateData;

// //asfifo
// asfifo #(
// 		 .data_width(8),
// 		 .address_width(11)		//depth 2048
// 		 ) U_rxfifo (
// 						.data_out (rx_fifo_data_out),
// 						.empty (rx_fifo_empty),
// 						.read_en (rx_fifo_rd_en),
// 						.clk_read (hclk),
						
// 						.data_in(Rxd_mac),
// 						.full(rx_fifo_full),
// 						.write_en(rx_fifo_wr_en),
// 						.clk_write(Rx_clk),
						
// 						.rst(~hresetn)
// 						);
////////////////////////////////////////////////////////////////////////////////////////////////////
// onu_tx

// reg 	  [7:0]   report_frame_data;
// always@(posedge Rx_clk)
//   report_frame_data <= $random;

// reg 			  send_report_frame_real, send_report_frame;
// reg 	  [7:0]   report_frame_cnt;

onu_tx U_onu_tx(
	.reset					(reset),
	.Gtx_clk 				(Gtx_clk),				//GMII interface 
	.Tx_en					(Tx_en),
	.Tx_er					(Tx_er),
	.Txd					(Txd_mac),

	.h_tx_data				(h_tx_data),
	.h_tx_data_ready 		(h_tx_data_ready & tx_grant_timeslot),

	.h_tx_data_rd 			(h_tx_data_rd),		//read signal (output) 

	.tx_state				(tx_state),
	.tx_byte_cnt			(tx_byte_cnt)
	);


// reg		  [7:0]   report_out;
// reg 			  report_out_en;
// // assign 			  h_tx_data_ready = report_out_en;
// integer 		  i 		  = 0;
// initial begin
//   #200 report_out_en = 1'b1;
//   repeat(20)
// 	@(posedge Gtx_clk) begin
// 	  report_out <= i;
// 	  i = i + 1;
// 	end
//   #2 report_out_en = 1'b0;
// end


// onu_mux U_onu_mux(
// 	.tx_fifo_bus			({~tx_fifo_empty, tx_fifo_data_out}),
// 	.report_bus				({1'b0, report_data_out}),
// 	.register_req_bus		({1'b0, register_req_data_out}),
// 	.register_ack_bus		({1'b0, register_ack_data_out}),
// 	.sel					(),
// 	.h_tx_data_ready		(h_tx_data_ready),
// 	.h_tx_data				(h_tx_data)
// 	);
wire 			  fifo_mpcp_sel;
wire 			  tx_mpcp_data_ready;
wire 	  [7:0]   tx_mpcp_data;
onu_mux U_onu_mux(
	.tx_fifo_bus			({~tx_fifo_empty, tx_fifo_data_out_q}),

	.tx_mpcp_bus			({tx_mpcp_data_ready, tx_mpcp_data}),
	.sel					(fifo_mpcp_sel),
	.h_tx_data_ready		(h_tx_data_ready),
	.h_tx_data				(h_tx_data)
	);

onu_mpcp U_onu_mpcp(
	.reset					(reset),
	.Rx_clk					(Rx_clk),
	.Rxd					(Rxd_mac),
	.rx_state				(rx_state),
	.rx_byte_cnt			(rx_byte_cnt),
	.rx_fcs_ok				(rx_fcs_ok),
	.h_tx_data_rd			(h_tx_data_rd),

	.Gtx_clk				(Gtx_clk),
	.tx_state				(tx_state),
	.tx_byte_cnt 			(tx_byte_cnt),

	.tx_mpcp_data_ready		(tx_mpcp_data_ready), 	//output
	.tx_mpcp_data			(tx_mpcp_data), 		//output

	.tx_grant				(tx_grant),
	.tx_grant_timeslot		(tx_grant_timeslot),
	
	.tx_fifo_rd_en			(tx_fifo_rd_en),
	.fifo_mpcp_sel			(fifo_mpcp_sel),

	.rx_frame_type			(rx_frame_type),
	
	.LocalTimeStamp			(LocalTimeStamp),
	.LLID					(LLID),
	.LOCAL_MAC_ADDR			(LOCAL_MAC_ADDR)
	);




// 	// .TxStartFrm				(TxStartFrm),
// 	// .TxEndFrm				(TxEndFrm),

// 	// .TxStartFrm				(~tx_fifo_empty),
// 	// .TxEndFrm				(tx_fifo_almost_empty),

	
// 	// .TxStartFrm				(~tx_fifo_empty & tx_grant),
// 	// .TxEndFrm				(tx_fifo_empty),

// 	.TxStartFrm				( report_frame_cnt == 8'd1 | (~tx_fifo_empty & tx_grant)),
// 	.TxEndFrm				((report_frame_cnt==8'd19) | tx_fifo_empty),
	

// 	.TxUnderRun				(1'b0),

// 	.h_tx_data				(send_report_frame_real ? report_frame_data : tx_fifo_data_out_q), //from fifo  and delayed one clk
	
// 	// .h_tx_data				(tx_fifo_data_out_q), 	//from fifo  and delayed one clk
// 	.StatePreamble			(),
// 	.StateData				()
// 	);

// // assign 			  tx_fifo_rd_en = U_onu_tx.StateData;
// assign 			  tx_fifo_rd_en = U_onu_tx.StartData | U_onu_tx.StateData; //resolve the empty flag delayed problem


// //for debug

// reg 			  debug, debug1;

// always@(posedge Rx_clk or posedge reset)
// begin
//   if(reset) begin
// 	debug <= 1'b0;
// 	debug1 <= 1'b0;
// 	send_report_frame_real <= 1'b0;
// 	send_report_frame <= 1'b0;
// 	report_frame_cnt <= 8'h00;
//   end
//   else
//   begin
// 	send_report_frame <= 1'b0;
// 	if(~send_report_frame_real & ~tx_fifo_empty & tx_grant)
// 	  debug <= 1'b1;

// 	// if(debug & tx_fifo_empty)
// 	// if(debug & ~Tx_en)
// 	  // debug1 <= 1'b1;
	
// 	if(debug1 & U_onu_tx.StartIdle)
// 	begin
// 	  send_report_frame_real <= 1'b1;
// 	  send_report_frame <= 1'b1;
// 	  debug1 <= 1'b0;
// 	end

// 	if(send_report_frame_real)
// 	begin
// 	  report_frame_cnt <= report_frame_cnt + 8'd1;
// 	  // if(report_frame_cnt == 8'd19)
// 	  // 	send_report_frame_real <= 0;
// 	end
//   end
// end

// //send report frame




// ////////////////////////////////////////////////////////////////////////////////////////////////////
// // //PHY
// // onu_phy U_onu_phy(
// // 	.Gtx_clk				(Gtx_clk)				, //used only in GMII mode
// // 	.Tx_en 					(Tx_en)					,
// // 	.Tx_er 					(Tx_er)					,
// // 	.Txd					(Txd)					,
	
// // 	.Rx_clk					(Rx_clk)					,
// // 	.Rx_dv					(Rx_dv)					,
// // 	.Rx_er					(Rx_er)					,
// // 	.Rxd					(Rxd)					,
	
// // 	.Col					(Col)						,	
// // 	.Crs					(Crs)						
// // 	);

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// // RS  -- supply the Gtx_clk
onu_rs U_onu_rs(
	.reset					(reset),
	.Gtx_clk				(Gtx_clk)				, //used only in GMII mode
	.Tx_en 					(Tx_en)					,
	.Tx_er 					(Tx_er)					,
	.Txd_mac 				(Txd_mac)					,
	
	.Rx_clk					(Rx_clk)					,
	.Rx_dv					(Rx_dv)					,
	.Rx_er					(Rx_er)					,
	.Rxd					(Rxd)					,
	
	.Col					(Col)						,	
	.Crs					(Crs)						,
	//the output to PHY
	.Txd					(Txd),
	.Rxd_mac				(Rxd_mac),
	.Rx_dv_mac				(Rx_dv_mac),

	.LocalLLID					(LLID)
	);
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// wire 			  hsel ;
// assign 			  hsel 		  = 1;
// ahb_ram_if U_ahb_ram(
// 			  .hresetn(hresetn),
// 			  .hclk(hclk),
// 			  .hsel(hsel),	
// 			  .haddr(haddr),
// 			  .htrans(htrans),
// 			  .hwrite(hwrite),
// 			  .hsize(hsize),
// 			  .hburst(hburst),
// 			  .hprot(hprot),
// 			  .hwdata(hwdata),

// 	.hready					(hready),
// 	.hresp					(hresp),
// 	.hrdata					(hrdata) 
// );

// // always@(posedge hclk or negedge hresetn)
// // begin
// //   if(!hresetn)
// // 	$ahb_ram_init();
// //   else 
// // 	$ahb_ram(haddr, htrans, hwrite, hsize, hburst, hprot, hwdata, hresp, hrdata);		//sel is not used
// // end


// // initial begin
// // hclk = 0;
// // forever #3 hclk = ~hclk;
// // end

// // initial begin
// // hresetn = 0; LatchTx = 0;
// // #10 hresetn = 1;

// // // #100 LatchTx = 1;
// // #10 LatchTx = 0;
// // end

// // initial begin
// // hresp = 2'b00;
// // forever #10 #1 hrdata = $random;
// // end




// initial
// begin
//   OLTTimeStamp <= 32'd0;
//   LocalTimeStamp <= 32'd0;
//   OLT_Grant1_Len <= 16'd0;
//   OLT_Grant1_Time <= 32'd0;
//   OLT_Grant_Num <= 8'd0;
// end


endmodule // onu_mac



